package com.whut.entryservice.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.whut.clcommon.entity.Result;
import com.whut.cocservice.domain.po.Village;
import com.whut.cocservice.service.COCService;
import com.whut.entryservice.domain.dto.CancelEntryDTO;
import com.whut.entryservice.domain.dto.EntryDTO;
import com.whut.entryservice.domain.moudle.EntryInfo;
import com.whut.entryservice.domain.po.Entry;
import com.whut.entryservice.domain.vo.AllEntryVO;
import com.whut.entryservice.domain.vo.EntryVO;
import com.whut.entryservice.mapper.EntryMapper;
import com.whut.entryservice.mq.producer.RabbitProducer;
import com.whut.entryservice.service.IEntrySecKillService;
import com.whut.entryservice.service.IEntryService;
import lombok.RequiredArgsConstructor;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.annotation.Reference;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.SessionCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import static com.whut.entryservice.constant.RedisConstants.*;

/**
 * @Description: 部落联赛报名Service实现类
 * @Author: Zonghe Wang
 * @Date: 2025/5/4 14:35
 */
@Service
@RequiredArgsConstructor
public class EntryServiceImpl extends ServiceImpl<EntryMapper, Entry> implements IEntryService {

    private final EntryMapper entryMapper;

    private final RedissonClient redissonClient;

    private final StringRedisTemplate stringRedisTemplate;

    private final IEntrySecKillService entrySecKillService;

    private final RabbitProducer rabbitProducer;

    @Reference
    private COCService cocService;

    private static final DefaultRedisScript<Long> ENTRY_SCRIPT;

    static {
        ENTRY_SCRIPT = new DefaultRedisScript<>();
        ENTRY_SCRIPT.setLocation(new ClassPathResource("lua/entry.lua"));
        ENTRY_SCRIPT.setResultType(Long.class);
    }

    @Override
    public Result entry(EntryDTO entryDTO) {
        // 1. 执行lua脚本 0-成功 1-名额不足 2-重复报名
        Long result = stringRedisTemplate.execute(
                ENTRY_SCRIPT,
                Collections.emptyList(),
                entryDTO.getClanNo(),
                entryDTO.getVillageNo(),
                entryDTO.getUid()
        );
        if (result == null) {
            log.error("entry.lua脚本执行异常");
            return Result.error("服务器出问题啦！");
        }
        // 2. 判断结果是否为0
        int r = result.intValue();
        if (r != 0) {
            return Result.error(r == 1 ? "名额不足" : "重复报名");
        }
        // 3. 有购买资格，发送mq消息
        rabbitProducer.sendEntryMessage(JSONUtil.toJsonPrettyStr(entryDTO));
        return Result.success();
    }

    @Override
    @Transactional
    public boolean createEntry(Entry entry) {
        String VillageNo = entry.getVillageNo();
        // 创建锁对象
        RLock lock = redissonClient.getLock(LOCK_ENTRY_KEY + VillageNo);
        // 尝试获取锁
        boolean isLock = lock.tryLock();
        if (!isLock) {
            // 获取锁失败，不允许重复下单
            log.error("获取锁失败，不允许重复下单");
            return false;
        }

        try {
            // 1.查询是否报过名
            Entry e = lambdaQuery().eq(Entry::getVillageNo, VillageNo).one();
            // 报过名
            if (e != null) {
                // 村庄已经报过名
                log.warn("村庄已经报名联赛");
                return false;
            }
            // 2.减少名额
            boolean success = entrySecKillService.reduceQuota(entry.getClanNo());
            if (!success) {
                // 扣减失败
                log.error("库存不足");
                return false;
            }
            // 3.创建报名
            this.save(entry);
        } finally {
            // 释放锁
            lock.unlock();
        }

        return true;
    }

    @Override
    public EntryVO selectEntryByClanNo(String ClanNo) {
        List<Entry> list = lambdaQuery()
                .eq(Entry::getClanNo, ClanNo)
                .orderByAsc(Entry::getCreateTime)
                .list();

        Set<String> villageSet = cocService.getVillagesByClanNo(ClanNo).stream()
                .map(Village::getVillageNo)
                .collect(Collectors.toSet());

        List<EntryInfo> infos = list.stream()
                .map(entry -> {
                    EntryInfo info = BeanUtil.copyProperties(entry, EntryInfo.class);
                    info.setPresent(villageSet.contains(entry.getVillageNo()));
                    return info;
                })
                .collect(Collectors.toList());
        return EntryVO.builder()
                .clanNo(ClanNo)
                .entries(infos)
                .build();
    }

    @Override
    @Transactional
    public void cancelEntry(CancelEntryDTO dto) {
        // 1. 删除数据库记录
        boolean success = lambdaUpdate()
                .eq(Entry::getVillageNo, dto.getVillageNo())
                .eq(Entry::getClanNo, dto.getClanNo())
                .remove();
        if (!success) {
            log.error("删除报名记录失败");
            return;
        }

        // 注册事务提交后的回调
        TransactionSynchronizationManager.registerSynchronization(
                new TransactionSynchronization() {
                    @Override
                    public void afterCommit() {
                        try {
                            // 2. 恢复名额、恢复报名set
                            executeRedisOperations(dto);
                        } catch (Exception e) {
                            // 3. 失败加入重试队列
                            retryRedisOperations(dto);
                            log.error("取消报名失败，加入重试队列");

                        }
                    }
                }
        );
    }

    public void executeRedisOperations(CancelEntryDTO dto) {
        // Redis事务
        stringRedisTemplate.execute(new SessionCallback<Object>() {
            @Override
            public Object execute(RedisOperations operations) {
                operations.multi();
                operations.opsForValue().increment(ENTRY_STOCK_KEY + dto.getClanNo(), 1);
                operations.opsForSet().remove(ENTRY_FIGHT_KEY + dto.getClanNo(), dto.getVillageNo());
                operations.exec();
                return null;
            }
        });
    }

    public void retryRedisOperations(CancelEntryDTO dto) {
        // 发出消息，实现异步重试
        rabbitProducer.sendCancelEntryMessage(JSONUtil.toJsonPrettyStr(dto));
    }


    @Override
    public AllEntryVO getAllEntry(Long bigClanId) {
        // TODO 根据大部落id获取所有部落联赛报名信息
        List<Entry> list = lambdaQuery().eq(Entry::getBigClanId, bigClanId).list();

        List<EntryVO> entryVOs = list.stream()
                .map(entry -> BeanUtil.copyProperties(entry, EntryVO.class))
                .collect(Collectors.toList());


        AllEntryVO.builder()
                .bigClanId(bigClanId)
                .clanNum(list.size())
                .entryVOs(entryVOs)
                .build();
        return null;
    }
}
